
package com.esdk.utils;

import cn.hutool.core.util.StrUtil;
import com.esdk.esdk;
import com.esdk.exception.SdkRuntimeException;
import com.esdk.interfaces.Runner;
import com.esdk.sql.SQLRuntimeException;
import lombok.NonNull;
import oshi.SystemInfo;
import oshi.hardware.Baseboard;
import oshi.hardware.HardwareAbstractionLayer;

import java.io.PrintWriter;
import java.io.Serializable;
import java.io.StringWriter;
import java.lang.management.ManagementFactory;
import java.lang.reflect.InvocationTargetException;
import java.sql.SQLException;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class EasyTool{
	public static boolean PrintShowCaller=true;
	public static String InfoCallerTemplate="({F}:{L})";
	public static String ErrorCallerTemplate="{M}({F}:{L})";
	static{
		PrintShowCaller=esdk.prop.getBoolean("PrintShowCaller",true);
		InfoCallerTemplate=esdk.obj.or(esdk.prop.get("INFOCallerTemplate"),InfoCallerTemplate);
		ErrorCallerTemplate=esdk.obj.or(esdk.prop.get("ErrorCallerTemplate"),ErrorCallerTemplate);
	}

	public static Throwable getRootCause(Throwable e){
		if(e==null)
			return null;
		return e.getCause()!=null?getRootCause(e.getCause()):e;
	}


	/**得到程序目录*/
	public static String getAppPath(){
		return System.getProperty("user.dir");
	}

	public static String getAppPath(String subPath){
		return esdk.file.getDirPath(System.getProperty("user.dir")+"/"+subPath);
	}

	public static String occurID(){
		return occurID(18);
	}

	private static HashSet _occurIdSet=new HashSet(50);
	private static String _lastTime;

	public static String occurID(int len){
		String result=_occurId(len);
		while(_occurIdSet.contains(result)){
			result=_occurId(len);
		}
		_occurIdSet.add(result);
		return result;
	}

	private static String _occurId(int len){
		final int minlen=15;
		String mstime=new SimpleDateFormat("yyMMddHHmmssSSS").format(new Date());
		String result=(mstime+Math.random()).replaceFirst("\\.","");
		if(len>minlen)
			result=result.substring(0,len);
		else
			result=result.substring(minlen-len,minlen);
		if(!mstime.equals(_lastTime)){
			_lastTime=mstime;
			_occurIdSet.clear();
		}
		return result;
	}

	public static String getRunPath(){
		return (String)System.getProperties().get("user.dir");
	}

	public static String getSystemEnvironment(){
		String result="";
		java.util.Properties props=System.getProperties();
		java.util.Enumeration e=props.propertyNames();
		for(;e.hasMoreElements();){
			String name=(String)e.nextElement();
			result+=name+" --- "+(String)props.get(name)+"\n";
		}
		try{
			java.net.InetAddress s=java.net.InetAddress.getLocalHost();
			result+="computer:"+s.getHostName()+"\n";
			return result;
		}catch(Exception ee){
			return ee.toString();
		}
	}

	public static boolean isWindowsSystem() {
		String OS=System.getProperty("os.name").toLowerCase();
		return (OS.indexOf("windows")>=0);
	}
	
	public static String getPropertiesValueWithSystemEnvirnment(String value){
		String result=esdk.str.format(value,System.getProperties());
		return result.replaceAll("\\$\\{.*\\}","");
	}

	@SuppressWarnings("unused")
	/**空方法，什么都不做*/
	public static void nothing(Object...o){
	}

	/**执行命令行，返回int类型的结果状态码*/
	public static int exec2Int(@NonNull String cmd){
		return EasyRuntime.exec2Int(cmd);
	}

	/**执行命令行，返回命令行执行过程中打印的字符串*/
	public static String exec2Str(@NonNull String cmd){
		return EasyRuntime.exec(cmd);
	}

	/**执行命令行，返回命令行执行过程中打印的字符串*/
	public static String exec(@NonNull String cmd){
		return EasyRuntime.exec(cmd);
	}
	/**命令行返回的结果对每行检查，如果有英文冒号则认为是有意义的返回内容，返回给调用者进行分析处理*/
	public static Map<String,String> exec2Map(String cmd,String... params){
		String res=EasyRuntime.exec(cmd,params);
		return resToMap(res);
	}

	public static void printJavaEnvirment(){
		Properties p=System.getProperties();
		Set keys=p.keySet();
		for(Object k:keys){
			esdk.sout(String.format("%s ==> %s",k,p.getProperty((String)k)));
		}
	}

	public static Map<String,String> resToMap(@NonNull String response){
		LinkedHashMap<String, String> result = new LinkedHashMap();
		String[] lines = response.split("\r?\n");
		String[] var4 = lines;
		int var5 = lines.length;

		for(int var6 = 0; var6 < var5; ++var6) {
			String item = var4[var6];
			String[] arr = item.split(":|=",2);
			if (arr.length == 2) {
				result.put(arr[0].trim(), arr[1].trim());
			}
		}

		return result;
	}

	public static String getSystemEncoding(){
		//		 esdk.sout("file.encoding:"+System.getProperty("file.encoding"));
		/*String encoding;
		if(System.getProperty("os.name").startsWith("Win"))
			encoding="gbk";
		else
			encoding=System.getProperty("file.encoding");
		return encoding.equals("MS950")?"big5":encoding;*/
		return System.getProperty("sun.jnu.encoding");
	}

	public static @NonNull String getFileEncoding(){
		return System.getProperty("file.encoding");
	}

	public static @NonNull String getExceptionStackTrace(@NonNull Throwable e){
		StringWriter sw=new StringWriter();
		e.printStackTrace(new PrintWriter(sw));
		return sw.toString();
	}

	public static @NonNull String getExceptionStackTrace(Throwable e,String includesRegex){
		return getExceptionStackTrace(e,includesRegex,null);
	}

	/**isQuickCheck为true：过滤不重要的StackTrace，只保留与项目相关的StackTrace
	 * @throws Exception */
	public static @NonNull String getExceptionStackTrace(@NonNull Throwable e,String includes,String excludes){
		StringWriter sw=new StringWriter();
		e.printStackTrace(new PrintWriter(sw));
		String result=sw.toString();
		boolean[] firstTrackLine=new boolean[]{true};
		String includesRegex=".*?\\.("+esdk.obj.or(includes,"").replaceAll(",","|")+").*";
		String excludesRegex=".*?\\.("+esdk.obj.or(excludes,"").replaceAll(",","|")+").*";
		if(esdk.str.isValid(includes)|| esdk.str.isValid(excludes)){
			result=esdk.regex.replaceAll(result,"(	at )(.*)(\r?\n)",matcher->{
			if(firstTrackLine[0]){
				firstTrackLine[0]=false;
				return matcher.group();
			}else if(esdk.str.isValid(excludes)&&matcher.group(2).matches(excludesRegex))
				return "";
			else if(matcher.group(2).matches(includesRegex))
				return matcher.group();
			else
				return "";
			});
		}
		return result;
	}

	public static @NonNull String getThrowableStackTrace(@NonNull Throwable e,String includes,String excludes){
		StringWriter sw=new StringWriter();
		e.printStackTrace(new PrintWriter(sw));
		String pst=sw.toString();
		StringBuffer result=new StringBuffer();
		Pattern p=Pattern.compile("(	at )(.*)(\r?\n)",0);
		String includesRegex=".*?\\.("+esdk.obj.or(includes,"").replaceAll(",","|")+").*";
		String excludesRegex=".*?\\.("+esdk.obj.or(excludes,"").replaceAll(",","|")+").*";
		Matcher matcher=p.matcher(pst);
		if(esdk.str.isValid(includes)|| esdk.str.isValid(excludes)){
			while(matcher.find()){
				String replacement;
				if(StrUtil.isNotBlank(excludes)&&matcher.group(2).matches(excludesRegex))
					replacement="";
				else if(!matcher.group(2).matches(includesRegex))
					replacement="";
				else
					replacement=matcher.group().replaceAll("\\$","\\\\\\$");
				matcher.appendReplacement(result,replacement);
			}
			matcher.appendTail(result);
		}else{
			result=new StringBuffer(pst);
		}
		return result.toString();
	}

	/**@param isRight 判断是否正确，如果为false则抛异常*/
	public static void throw4False(boolean isRight,@NonNull String errMsg){
		if(isRight==false)
			throwException(errMsg);
	}

	/**
	 * @param isError 判断是否错误，如果为true则抛异常
	 * */
	public static void throw4Error(boolean isError,@NonNull String errMsg){
		if(isError)
			throwException(errMsg);
	}

	/**
	 * 用运行时异常包裹抛出对象，如果抛出对象本身就是运行时异常，则直接返回。
	 * 如果是 InvocationTargetException，那么将其剥离，只包裹其 TargetException
	 * @param e 抛出对象
	 * @return 运行时异常
	 */
	public static RuntimeException wrapThrowble(@NonNull Throwable e){
		if(e instanceof SQLException)
			return new SQLRuntimeException(e);
		else if(e instanceof RuntimeException re)
			return re;
		else if(e instanceof InvocationTargetException ite)
			return wrapThrowble(ite.getTargetException());
		return new SdkRuntimeException(e);
	}

	public static void throwCatch(@NonNull Throwable e){
		throw wrapThrowble(e);
	}

	public static void throwException(@NonNull String errMsg){
		throw new SdkRuntimeException(errMsg);
	}

	public static <T extends Exception> void throwOnFalse(boolean throwOnFalse,@NonNull String errorMsg,Class<T> exceptionCls)throws Exception{
		if(!throwOnFalse)
			throw esdk.reflect.safeNewInstance(exceptionCls,errorMsg);
	}

	public static void throwOnFalse(boolean throwOnFalse,@NonNull String errorMsg){
		if(throwOnFalse==false)
			throw new AssertException(errorMsg);
	}
	public static void throwOnTrue(boolean throwOnTrue,@NonNull String errorMsg){
		if(throwOnTrue)
			throw new AssertException(errorMsg);
	}

	public static boolean assertEquals(boolean v){
		return TestHelper.assertEquals(Boolean.valueOf(v),Boolean.TRUE);
	}

	public static boolean assertEquals(double actualValue,double expectValue){
		return TestHelper.assertEquals(Double.valueOf(actualValue),Double.valueOf(expectValue));
	}

	public static boolean assertEquals(long actualValue,long expectValue){
		return TestHelper.assertEquals(actualValue,expectValue);
	}
	
	public static boolean assertEquals(int actualValue,int expectValue){
		return TestHelper.assertEquals(Integer.valueOf(actualValue),Integer.valueOf(expectValue));
	}

	public static boolean assertEquals(Object actualValue,Object expectValue){
		return TestHelper.assertEquals(actualValue,expectValue);
	}

	/**忽略大小写*/
	public static boolean aeic(String actualValue,String expectValue){
		return TestHelper.assertEqualsIgnoreCase(actualValue,expectValue);
	}

	/**忽略大小写，与aeic相同*/
	public static boolean assertEqualsIgnoreCase(String actualValue,String expectValue){
		return TestHelper.assertEqualsIgnoreCase(actualValue,expectValue);
	}

	public static boolean assertEquals(String actualValue,String expectValue,boolean ignoreCase){
		if(ignoreCase)
			return TestHelper.assertEqualsIgnoreCase(actualValue,expectValue);
		else
			return assertEquals(actualValue,expectValue);
	}

	public static boolean assertEquals(String actualValue,String expectValue){
		return TestHelper.assertEquals(actualValue,expectValue);
	}

	public static void printAssertInfo(){
		TestHelper.printAssertInfo();
	}

	public static boolean asSubClass(Class child,Class parent){
		return parent.isAssignableFrom(child);
	}

	@SuppressWarnings("unused")
	public static void read(Object...o){
	}

	@SuppressWarnings("unused")
	public static void read(Object o){
	}
	
	public static void sleep(long millis){
		try{
//			TimeUnit.MILLISECONDS.sleep(millis);
			Thread.currentThread().sleep(millis);
		}catch(InterruptedException e){
			e.printStackTrace();
		}
	}
	public static void awaitTermination(@NonNull ThreadPoolExecutor executor) {
		ThreadUtils.awaitTermination(executor);
	}
	public static void awaitTermination(@NonNull ThreadPoolExecutor executor,long sleepSec) {
		ThreadUtils.awaitTermination(executor,sleepSec,TimeUnit.SECONDS);
	}
	public static void shutdownOnCompleted(@NonNull ThreadPoolExecutor executor) {
		ThreadUtils.shutdownOnCompleted(executor);
	}

	public static void shutdownOnCompleted(@NonNull ThreadPoolExecutor executor,long waitSleepSec) {
		ThreadUtils.shutdownOnCompleted(executor,waitSleepSec);
	}

	public static String printUpTime(){
		return printUpTime("程序已运行时长：{}");
	}

	public static String printUpTime(@NonNull String template){
		String elapse=esdk.time.formatElapse(ManagementFactory.getRuntimeMXBean().getUptime());
		esdk.sout(template,elapse);
		return elapse;
	}

	public static void IF(boolean ifVal,Runner thenCB){
		try{
			if(ifVal)
				thenCB.run();
		}catch(Exception e){
			throw wrapThrowble(e);
		}
	}

	public static void IF(boolean ifVal,Runner thenCB,Runner elseCB){
		try{
			if(ifVal)
				thenCB.run();
			else if(elseCB!=null)
				elseCB.run();
		}catch(Exception e){
			throw wrapThrowble(e);
		}
	}

	public static String getCaller(){
		return getCaller(InfoCallerTemplate,5);
	}

	public static String getCaller(@NonNull String template){
		return getCaller(template,5);
	}

	public static String getCaller(@NonNull String template,int index){
		if(PrintShowCaller){
			StackTraceElement stack=getStackTrace(index+1);
			Map<String,? extends Serializable> params=Map.of("C",esdk.str.getClassShortName(stack.getClassName()),"M",stack.getMethodName(),"F",esdk.obj.or(stack.getFileName(),()->esdk.obj.getClassSimpleName(stack.getClassName())),"L",stack.getLineNumber());
			String result=esdk.str.format(template,params);
			return result;
		}
		else
			return "";
	}

	public static StackTraceElement getStackTrace(int index){
		StackTraceElement[] stacks=Thread.currentThread().getStackTrace();
		StackTraceElement stack=stacks[stacks.length-1];
		for(int i=index;i<stacks.length;i++){
			stack=stacks[i];
			if(stack.getMethodName().contains("main")||
//				stack.getMethodName().contains("lambda$")||
				(!stack.getMethodName().matches("assertEquals|printStatus")&&
					!stack.getClassName().matches("(^com\\.esdk\\.(sql|log|utils).+)|(^.+orm\\.[a-zA-Z]+Select$)|^.+(BaseDao|ModelDao|ModelModule|esdk|PlumeLog|SQLAssistant|EasyTool|ThreadUtils)"))
			)
				break;
		}
		return stack;
	}

	public static String getBoardSN(){
		// 创建 SystemInfo 对象
		SystemInfo systemInfo = new SystemInfo();
		// 获取硬件抽象层
		HardwareAbstractionLayer hardware = systemInfo.getHardware();
		// 获取主板信息类
		Baseboard board = hardware.getComputerSystem().getBaseboard();
		return board.getSerialNumber();
	}

	public static String getDiskSerial(){
		// 创建 SystemInfo 对象
		SystemInfo systemInfo = new SystemInfo();
		// 获取硬件抽象层
		HardwareAbstractionLayer hardware = systemInfo.getHardware();
		// 获取主板信息类
		return hardware.getDiskStores().getFirst().getSerial();
	}

	public static long getMachineID(){
		String machineSn=getBoardSN()+getDiskSerial();
		String machineId=esdk.str.or(machineSn.replaceAll("[^0-9]*",""),machineSn.hashCode()+"");
		return Long.valueOf(machineId);
	}

}
